package lintulaskenta.validointi;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lintulaskenta.entity.Havainnointitapahtuma;
import lintulaskenta.entity.Reitti;
import lintulaskenta.rekisteri.ReittiRekisteri;

//@author jjnyman & Pajunen & Konola
public class TietokannanKenttienValidointi {

    private TipuApiValidator validator;

    public TietokannanKenttienValidointi() throws FileNotFoundException, IOException {
        validator = new TipuApiValidator();
    }

    public String tarkistaLintulajinLyhenne(String lintu) {
        if (lintu.length() != 6) {
            //System.out.println(haettavaLintu);
            return "Lintulaji on oltava täsmälleen 6 merkkiä! ";
        }
        if (validator.tarkistaLintulaji(lintu) == false) {
            return "Ehdottamasi lintulaji on virheellinen! ";
        }
        return "true";
    }

    public String tarkistaKunnanLyhenne(String kunta) {
        if (kunta.length() > 6) {
            //System.out.println(haettavaKunta);
            return "Kunnan lyhenne on maksimissaan kuusi merkkiä! ";
        }
        if (validator.tarkistaKunta(kunta) == false) {
            return "Kyseisellä lyhenteellä ei löydy kuntaa kannasta! ";
        }
        return "true";
    }
    /*
     * Jos parven kooksi ehdotetaan yli 100kpl, tulee herjaa
     */

    public String tarkistaParvenKoko(String parvenKoko) {
        if (parvenKoko == null) {
            return "Parven koko on null! ";
        }
        int koko;

        try {
            koko = Integer.parseInt(parvenKoko);
        } catch (Exception e) {
            //e.printStackTrace();
            return "Parven koko ei ole numeraali! ";
        }

        if (koko > 100) {
            return "Oletko varma että parven koko on yli 100 yksilöä? ";
        }
        if (koko < 1) {
            return "Yrität syöttää parven kooksi nollaa tai negatiivista! ";
        }
        return "true";
    }

    /*
     * Simppeli metodi joka tarkistaa onko säde muuta kuin 50 tai 25m
     */
    public String tarkistaSade(String tarkistettava) {
        if (tarkistettava == null) {
            return "Säde on null! ";
        }
        int sade;

        try {
            sade = Integer.parseInt(tarkistettava);
        } catch (Exception e) {
            //e.printStackTrace();
            return "Säde ei ole numeraali! ";
        }

        if (sade != 50 || sade != 25) {
            return "Säde on oltava joko 25 tai 50 metriä! ";
        }
        return "true";
    }

    /* Ohjeiden mukaan laskenta tulisi suorittaa ennen kello 10 aamulla
     * joten vingutaan jos tuntuu menevän liian myöhäiseksi.
     * Ei kannata tehdä tarkaksi ehdoksi, mutta tarkistusfunkkari joka
     * voi huomauttaa asiasta.
     */
    public String tarkistaLaskennanPäättymisaika(String tarkistettava) {
        if (tarkistettava == null) {
            return "Aika on null! ";
        }
        int aika;

        try {
            aika = Integer.parseInt(tarkistettava);
        } catch (Exception e) {
            //e.printStackTrace();
            return "Aika ilmoitettava numeroina ";
        }
        if (aika > 9) {
            return "Päättyikö laskenta vasta klo " + tarkistettava + "? ";
        }
        if (aika < 6) {
            return "Päättyikö laskenta jo klo " + tarkistettava + "? ";
        } else {
            return "true";
        }
    }

    public String tarkistaKuntaJaKoordinaatit(String kunta, String lati, String longi) throws IOException {
        int latit;
        int longit;
        try {
            latit = Integer.parseInt(lati);
            longit = Integer.parseInt(longi);
        } catch (Exception e) {
            //e.printStackTrace();
            return "Koordinaatit ilmoitettava numeroina";
        }
        if (validator.tarkistapeninkulmaruutu(kunta, latit, longit) == true) {
            return "true";
        } else
        return "Kunta ja koordinaatit eivät matchaa: " + kunta + ":  P:" + lati + "  I:" + longi;
    }
    
    public String tarkistaLaskennanAlkamisaika(String tarkistettava) {
        if (tarkistettava == null) {
            return "Aika on null! ";
        }
        int aika;

        try {
            aika = Integer.parseInt(tarkistettava);
        } catch (Exception e) {
            //e.printStackTrace();
            return "Aika ilmoitettava numeroina ";
        }
        if (aika > 9) {
            return "Alkoiko laskenta vasta klo " + aika + "? ";
        }
        if (aika < 5) {
            return "Alkoiko laskenta jo klo " + aika + "? ";
        } else {
            return "true";
        }
    }

    public String tarkistaLahempiKunta(String tarkistettava) {
        if (tarkistettava == null) {
            return "Lähempi kunta ei voi olla null!";
        }

        if (validator.tarkistaLahempiPaikka(tarkistettava) == false) {
            return "Kunta on oltava pituudeltaan vähintään yksi kirjain ja maksimissaan 17 kirjainta ";
        }
        return "true";
    }

    //TietokannanKasittelija dbkasittelija = new TietokannanKasittelija();
    /***************************************************************************************
     *                                                                                    *
     * Kaksi eri funkkaria joilla kokeilla onko laskenta-tarkistettava uudessa havaintotapahtumassa*
     * hyväksyttävät ajankohdat ensimmäisen laskennan kannalta.                           *
     *                                                                                    *
     * Ensin kannattaa ajaa kaykoLaskentaPvm() ja jos se menee läpi, niin sitten tarkan   *
     * kellonajan tarkistus kaykoLaskentaAika(). Eri metodit, jotta voidaan myöhemmin     *
     * käyttää jäljittämiseen, että mikä oli vikana, eikä vain ilmoittaa geneerista failia*
     *                                                                                    *
     **************************************************************************************/
    /*
     * NYMAN, VOISIT TARKISTAA OLENKO YMMÄRTÄNYT METODIN TOIMINNAN OIKEIN!!
     *
     * Hakee tällä hetkellä reitin havaintokerrat ja järjestää ne kronologiseen
     * järjestykseen niin että uusin [0]...[n] vanhin
     * Vielä pitää päättää  miten tarkistettava esitetään ja tyrkätä se parametrinä sisään
     * sekä tehdä sille vertailu tietyn havaintokerran/-kertojen välillä. Tällä hetkellä
     * tarkistaa onko ensimmäisen havaintotapahtuman ja kokeiltavan tapahtuman
     * tuntimäärän välillä eroa yli 30 min. Palauttaa true jos ei, jos taas yli
     * niin false.
     */
    public String kaykoLaskentaAika(int reittiNro, int[] aika) throws Exception {

        List<Havainnointitapahtuma> havainnot = haeHavainnotJarjestyksessa(reittiNro);
        //Clean over clever. Selkeyden vuoksi kopsaan parametritaulukon muuttujanimiin.
        //Samoin jos halutaankin tuoda int[] sijasta kasa int parametreja, on nopea muuttaa
        //koodi sellaiseksi.
        if (aika.length != 5) {
            return "Aika väärässä muodossa! ";
        }
        int vuosi = aika[0], kuukausi = aika[1], paiva = aika[2];
        int tunti = aika[3], minuutti = aika[4];
        Havainnointitapahtuma ensimmainen = havainnot.get(havainnot.size() - 1);
        int erotus = 0;

        if (tunti == ensimmainen.getAlkuTunnit()) {
            erotus = Math.abs(minuutti - ensimmainen.getAlkuMinuutit());
            if (erotus > 30) {
                return "Havaintokertojen välillä on erotusta yli 30 minuuttia! ";
            }
            return "true";
        } else if (tunti < ensimmainen.getAlkuTunnit()) {
            int tuntierotus = ensimmainen.getAlkuTunnit() - tunti;
            erotus = ((60 * (tuntierotus - 1)) + (60 - minuutti)) + ensimmainen.getAlkuMinuutit();
            if (erotus > 30) {
                return "Havaintokertojen välillä on erotusta yli 30 minuuttia! ";
            }
            return "true";
        } else if (tunti > ensimmainen.getAlkuTunnit()) {
            int tuntierotus = tunti - ensimmainen.getAlkuTunnit();
            erotus = ((60 * (tuntierotus - 1)) + (60 - minuutti)) + minuutti;
            if (erotus > 30) {
                return "Havaintokertojen välillä on erotusta yli 30 minuuttia! ";
            }
            return "true";
        }


        return "Havaintokertojen välillä on erotusta yli 30 minuuttia! ";
    }

    /*
     * NYMAN, VOISIT TARKISTAA OLENKO YMMÄRTÄNYT METODIN TOIMINNAN OIKEIN!!
     *
     * Hakee tällä hetkellä reitin havaintokerrat ja järjestää ne kronologiseen
     * järjestykseen niin että uusin [0]...[n] vanhin
     * Vielä pitää päättää  miten tarkistettava esitetään ja tyrkätä se parametrinä sisään
     * sekä tehdä sille vertailu tietyn havaintokerran/-kertojen välillä. Tällä hetkellä
     * tarkistaa onko ensimmäisen havaintotapahtuman ja kokeiltavan tapahtuman päivämäärän
     * välillä eroa yli 7 vrk. Jos ei palauttaa true, muuten false
     */
    public String kaykoLaskentaPvm(int reittiNro, int[] aika) throws Exception {
        List<Havainnointitapahtuma> havainnot = haeHavainnotJarjestyksessa(reittiNro);
        //Clean over clever. Selkeyden vuoksi kopsaan parametritaulukon muuttujanimiin.
        //Samoin jos halutaankin tuoda int[] sijasta kasa int parametreja, on nopea muuttaa
        //koodi sellaiseksi.

        if (aika.length != 5) {
            return "Aika väärässä muodossa! ";
        }
        int vuosi = aika[0], kuukausi = aika[1], paiva = aika[2];
        int tunti = aika[3], minuutti = aika[4];
        Havainnointitapahtuma ensimmainen = havainnot.get(havainnot.size() - 1);
        int erotus = 0;

        if (kuukausi == ensimmainen.getKuukausi()) {
            erotus = Math.abs(ensimmainen.getPaiva() - paiva);
            if (erotus > 7) {
                return "Eroa ensimmäiseen havaintotapahtumaan yli 7 vuorokautta! ";
            }
            return "true";
        } else if (kuukausi < ensimmainen.getKuukausi()) {
            if (kuukausi == 5) {
                erotus = (31 - paiva) + ensimmainen.getPaiva();
                if (erotus > 7) {
                    return "Eroa ensimmäiseen havaintotapahtumaan yli 7 vuorokautta! ";
                }
                return "true";
            } else if (kuukausi == 6) {
                erotus = (30 - paiva) + ensimmainen.getPaiva();
                if (erotus > 7) {
                    return "Eroa ensimmäiseen havaintotapahtumaan yli 7 vuorokautta! ";
                }
                return "true";
            }
        } else if (kuukausi > ensimmainen.getKuukausi()) {
            if (kuukausi == 6) {
                erotus = (31 - ensimmainen.getPaiva()) + paiva;
                if (erotus > 7) {
                    return "Eroa ensimmäiseen havaintotapahtumaan yli 7 vuorokautta! ";
                }
                return "true";
            } else if (kuukausi == 7) {
                erotus = (30 - ensimmainen.getPaiva()) + paiva;
                if (erotus > 7) {
                    return "Eroa ensimmäiseen havaintotapahtumaan yli 7 vuorokautta! ";
                }
                return "true";
            }
        }
        return "Eroa ensimmäiseen havaintotapahtumaan yli 7 vuorokautta! ";
    }
//    public String tarkistaLaskennanPaivamaara(String tarkistettava) {
//
//
//        return "true";
//    }
    /*
     *   Vertailufunkkari jotta haettavan Havaintotapahtumalistan saa sortattua.
     */
    static final Comparator<Havainnointitapahtuma> Vuosijärjestys = new Comparator<Havainnointitapahtuma>() {

        public int compare(Havainnointitapahtuma h1, Havainnointitapahtuma h2) {
            Integer vuosi1 = new Integer(h1.getVuosi());
            Integer vuosi2 = new Integer(h2.getVuosi());
            return vuosi2.compareTo(vuosi1);
        }
    };

    /*
     * Epäoptimi tapa noukkia varmaankin, sillä varmasti tietokanta tukee hakua
     * jossa pyydetään jonkin reitin kaikki havaintotiedot. Voi olla myös iloa
     * mikäli halutaan esim. laskea keskiarvoa tjsp. eikä vain yhtä havaintotapahtumaa
     */
    public static List<Havainnointitapahtuma> haeHavainnotJarjestyksessa(int reittiNro) throws Exception {

        ReittiRekisteri reittiRekisteri = new ReittiRekisteri();
        Map<String, Object> reitinEhdot = new HashMap<String, Object>() {
        };
        reitinEhdot.put("vanhaReittiNumero", reittiNro);
        List<Reitti> haettuReitti = reittiRekisteri.haeReititEhdoilla(reitinEhdot);
        Reitti r = haettuReitti.get(0);
        //System.out.println("reitin r laskentakunta " + r.getLaskentakunta());
        List<Havainnointitapahtuma> havainnot = new ArrayList<Havainnointitapahtuma>();
        for (Havainnointitapahtuma havainto : r.getHavainnointitapahtumat()) {
            havainnot.add(havainto);
        }
        Collections.sort(havainnot, Vuosijärjestys);
        for (Havainnointitapahtuma h : havainnot) {
            System.out.println(h.getVuosi());
        }
        return havainnot;
    }

    /*
     * Tarkistaa onko kenttään syötetty String -arvo kokonaisluku
     */

    public String tarkistaInt(String str, String kenttanimi) {
        try {
            if (str.length() > 0) {
                if (Character.isWhitespace(str.charAt(0))) {
                    str = str.trim();
                }              
                int testi = Integer.parseInt((str));
            } else {                
                return "true";
            }
        } catch (NumberFormatException e) {

            return kenttanimi + " -kentän arvon on oltava numeerinen. ";

        }
        return "true";
    }
}
